package com.wxh.utils;

import java.util.Random;

/**
 * @ClassName: RandomEncryUtils
 * @Description: 随机数加解密算法
 * @Author wxh
 * @Date: 2020/7/28 17:43
 * @Version V1.0.0
 * @Since 1.8
 */
public final class RandomEncryUtils {
    /**
     * 默认字符
     */
    private static final byte[] DEFAULT_CHARS = { '!', '#', '&', '<', '>', '_', '|', '~' };

    private RandomEncryUtils(){

    }

    /**
     * 将十进制的数值转换为十六进制的数值的字节类型
     * byte类型的取值范围也是[-128, 127]
     * 一个字符等于两个字节
     * @param num 数值
     * @return byte 字节类型
     */
    private static byte dec2Hex(int num) {
        // 字符 0-9A-Z
        int n = 9;
        if(num > n) {
            // 字符A == 65
            num = num - 10 + 'A';
        }else{
            // 字符0 == 0x30 == 48
            num |= 0x30;
        }
        return (byte) num;
    }

    /**
     * 将十六进制的数值转换为十进制的数值
     * @param num 数值
     * @return int int类型
     */
    private static int hex2Dec(int num) {
        /**
         *  二进制   十进制   十六进制  ascii
         * 01000001	   65	 41	       A
         */
        int n = 65;
        if(num < n) {
            // 字符0 == 0x30 == 48
            num -= 0x30;
        }else{
            num = num + 10 - 65;
        }
        return num;
    }


    /**
     * 随机生成8位长度字节数组
     * @return byte[] 字节数组
     */
    private static byte[] generateRandom() {
        Random random = new Random();
        byte[] keyAry = new byte[8];
        // 将随机生成的数填充到字节数组中
        random.nextBytes(keyAry);
        return keyAry;
    }

    /**
     * 将需要编码的字节数组和包含某些特定字符的字节数组，生成新的字节数组
     * @param sourceAry 需要编码的字符串
     * @param keyAry 包含特定字符的字节数组
     * @return  byte[] 字节数组
     */
    private static byte[] encode(byte[] sourceAry, byte[] keyAry) {
        // 将新字节数组容量扩容一倍
        byte[] encodeAry = new byte[sourceAry.length * 2];
        int sourceAryLen = 0, keyAryLen = 0, i = 0, j = 0;
        if (sourceAry == null || keyAry == null || keyAry.length <= 0) {
            return null;
        }
        sourceAryLen = sourceAry.length;
        keyAryLen = keyAry.length;
        for (; i < sourceAryLen; i++) {
            byte ch = sourceAry[i];
            // sourceAry中的字符与keyAry相加
            ch += keyAry[i % keyAryLen];
            encodeAry[j++] = dec2Hex((ch >> 4) & 0xf);
            encodeAry[j++] = dec2Hex(ch & 0xf);
        }
        return encodeAry;
    }

    /**
     *  将编码后的字节数据解码为新的字节数组
     * @param encodedAry 编码后字节数组
     * @param keyAry 包含特定字符的字节数组
     * @return byte[] 字节数组
     */
    private static byte[] decode(byte[] encodedAry, byte[] keyAry) {
        int encodedAryLen = 0,keyAryLen = 0, i = 0, j = 0;
        int decodeAryLen = encodedAry.length / 2;
        // 解码字节数组
        byte[] decodeAry = new byte[decodeAryLen];
        if (encodedAry == null || keyAry == null || keyAry.length <= 0) {
            return null;
        }
        encodedAryLen = encodedAry.length;
        keyAryLen = keyAry.length;
        for (; i < encodedAryLen; i += 2) {
            int ch = ((hex2Dec(encodedAry[i]) << 4) & 0xf0) | hex2Dec(encodedAry[i + 1]);
            int chKey = keyAry[j % keyAryLen];
            ch -= chKey;
            decodeAry[j] = (byte) ch;
            j++;
        }
        return decodeAry;
    }

    /**
     *  将字符串加密
     * @param sourceStr 需要加密的字符串
     * @return String 字符串
     */
    public static String encrypt(String sourceStr) {
        byte[] encodeAry = null;
        byte[] temp = null;
        byte[] result = null;
        byte[] randomAry = new byte[8];
        if (sourceStr == null) {
            return null;
        }
        randomAry = generateRandom();
        temp = encode(randomAry, DEFAULT_CHARS);
        if (temp != null) {
            encodeAry = encode(sourceStr.getBytes(), randomAry);
            result = new byte[encodeAry.length + 16];
            System.arraycopy(temp, 0, result, 0, temp.length);
            System.arraycopy(encodeAry, 0, result, temp.length, encodeAry.length);
        }
        return new String(result);
    }

    /**
     *  将加密后字符串解密
     * @param encryptStr 加密后字符串
     * @return String
     */
    public static String decrypt(String encryptStr) {
        byte[] encodeAry = encryptStr.getBytes();
        if (encodeAry.length < 16) {
            return null;
        }
        byte[] sixteenAry = new byte[16];
        byte[] eightAry = new byte[8];
        byte[] temp = new byte[encodeAry.length - 16];
        byte[] decodeAry = new byte[(encodeAry.length - 16) / 2];
        System.arraycopy(encodeAry, 0, sixteenAry, 0, 16);
        eightAry = decode(sixteenAry, DEFAULT_CHARS);
        if (eightAry != null) {
            System.arraycopy(encodeAry, 16, temp, 0, encodeAry.length - 16);
            decodeAry = decode(temp, eightAry);
        }
        return new String(decodeAry);
    }

    public static void main(String[] args) {
        byte[] bytes = generateRandom();
        for (int i = 0; i < bytes.length; i++) {
            System.out.println("bytes["+i+"] = " + bytes[i]);
        }
        // -84 111 100 60 -41
        System.out.println("dec2Hex(100) = " + dec2Hex(8));
        System.out.println("hex2Dec(66) = " + hex2Dec(66));

        String message = "18612341234";
        String encryptStr = encrypt(message);
        System.out.println(message + " 加密后的字符串为: " + encryptStr);
        String decryptStr = decrypt(encryptStr);
        System.out.println(encryptStr +" 还原后的字符串为: " + decryptStr);
    }
}
